package com.pig4cloud.pigx.contract.executer.trade.handler.impl;

import com.pig4cloud.pigx.common.customize.enums.CommonDictEnum;
import com.pig4cloud.pigx.common.customize.mysql.utils.MysqlUtil;
import com.pig4cloud.pigx.common.customize.security.utils.SecurityUtil;
import com.pig4cloud.pigx.common.customize.utils.CommonUtil;
import com.pig4cloud.pigx.contract.bo.ContractTradeNewBO;
import com.pig4cloud.pigx.contract.entity.ContractOrder;
import com.pig4cloud.pigx.contract.entity.ContractPosition;
import com.pig4cloud.pigx.contract.executer.order.supporter.OrderCalculator;
import com.pig4cloud.pigx.contract.executer.position.supporter.PositionCalculator;
import com.pig4cloud.pigx.contract.executer.trade.handler.TradeHandlerManager;
import com.pig4cloud.pigx.contract.executer.trade.supporter.TradeCalculator;
import com.pig4cloud.pigx.contract.mapper.ContractOrderMapper;
import com.pig4cloud.pigx.contract.mapper.ContractPositionMapper;
import com.pig4cloud.pigx.contract.mapper.ContractTradeMapper;
import com.pig4cloud.pigx.contract.mongo.entity.ContractTradeMongo;
import com.pig4cloud.pigx.contract.utils.ContractUtils;
import com.pig4cloud.pigx.wallet.dto.WalletContractAccountChangeDTO;
import com.pig4cloud.pigx.wallet.enums.WalletContractAccountDetailEnum;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Objects;

/**
 * 用户订单撮合处理器
 *
 * @since 2022/8/17 21:26
 */
@Component
public class UserTradeHandler extends AbstractTradeHandler
        implements InitializingBean {

    protected final TradeHandlerManager tradeHandlerManager;

    /**
     * 有参构造函数
     * <p>同时给父级对象赋值
     *
     * @since 2022/8/19 14:47
     */
    public UserTradeHandler(ContractOrderMapper contractOrderMapper,
                            ContractTradeMapper contractTradeMapper,
                            ContractPositionMapper contractPositionMapper,
                            RedisTemplate redis,
                            MongoTemplate mongo,
                            CommonUtil commonUtil,
                            MysqlUtil mysqlUtil,
                            SecurityUtil securityUtil,
                            ContractUtils contractUtils,
                            PositionCalculator positionCalculator,
                            TradeHandlerManager tradeHandlerManager) {
        super(contractOrderMapper, contractTradeMapper, contractPositionMapper, redis, mongo, commonUtil, mysqlUtil, securityUtil, contractUtils, positionCalculator);
        this.tradeHandlerManager = tradeHandlerManager;
    }


    /**
     * 当前对象创建后
     *
     * @since 2022/8/17 14:36
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        // 注册到manager
        tradeHandlerManager.getHandlerList().add(this);
    }

    /**
     * 判断是否支持
     *
     * @param trade 成交记录
     * @return {@link boolean}
     * @since 2022/8/17 11:20
     */
    @Override
    public boolean support(ContractTradeMongo trade) {
        // 不是机器人
        return Objects.equals(CommonDictEnum.YesOrNo.NO.getValue(), trade.getIsRobot());
    }

    /**
     * 计算费用
     *
     * @param trade 成交记录
     * @since 2022/8/19 16:25
     */
    @Override
    protected void calculateFee(ContractTradeNewBO trade) {
        // 开仓
        if (trade.getIsOpen()) {
            // 手续费
            trade.setHandlingFee(OrderCalculator.getHandlingFee(trade.getTradeValue(), trade.getHandlingFeeRate()));
            // 组装手续费参数，需要扣除冻结金额
            WalletContractAccountChangeDTO profitParam = ContractUtils.getChangeAssetParamByTrade(trade,
                    trade.getHandlingFee(),
                    null,
                    WalletContractAccountDetailEnum.BusinessType.HANDLING_FEE,
                    WalletContractAccountDetailEnum.OperationType.MINUS,
                    true);
            trade.getChangeAmountParamList().add(profitParam);

            // 计算保证金
            BigDecimal margin = TradeCalculator.getMarginWhenOpen(trade);
            // 开仓时手续费从保证金中扣除
            trade.setMargin(margin.subtract(trade.getHandlingFee()));
            // 组装保证金参数
            WalletContractAccountChangeDTO marginParam = ContractUtils.getChangeAssetParamByTrade(trade,
                    trade.getMargin(),
                    null,
                    WalletContractAccountDetailEnum.BusinessType.POSITION_MARGIN,
                    WalletContractAccountDetailEnum.OperationType.ADD,
                    true);
            trade.getChangeAmountParamList().add(marginParam);

            // 解冻费用
            trade.setUnfreezeAmount(margin);
        } else {
            ContractPosition position = trade.getPosition();

            // 手续费
            trade.setHandlingFee(OrderCalculator.getHandlingFee(trade.getTradeValue(), trade.getHandlingFeeRate()));
            // 组装手续费参数
            WalletContractAccountChangeDTO handlingFeeParam = ContractUtils.getChangeAssetParamByTrade(trade,
                    trade.getHandlingFee(),
                    null,
                    WalletContractAccountDetailEnum.BusinessType.HANDLING_FEE,
                    WalletContractAccountDetailEnum.OperationType.MINUS,
                    false);
            trade.getChangeAmountParamList().add(handlingFeeParam);

            // 收益
            trade.setProfit(OrderCalculator.getProfit(trade.getPrice(),
                    position.getOpenAvgPrice(),
                    trade.getNum(),
                    trade.getSide()));
            // 组装收益参数
            WalletContractAccountChangeDTO profitParam = ContractUtils.getChangeAssetParamByTrade(trade,
                    trade.getProfit().abs(),
                    null,
                    WalletContractAccountDetailEnum.BusinessType.PROFIT,
                    ContractUtils.getProfitOperationType(trade.getProfit()),
                    false);
            trade.getChangeAmountParamList().add(profitParam);

            // 保证金，平仓时根据持仓数量、成交数量、持仓保证金计算
            trade.setMargin(TradeCalculator.getMarginWhenClose(trade));
            // 组装保证金参数
            WalletContractAccountChangeDTO marginParam = ContractUtils.getChangeAssetParamByTrade(trade,
                    trade.getMargin(),
                    null,
                    WalletContractAccountDetailEnum.BusinessType.POSITION_MARGIN,
                    WalletContractAccountDetailEnum.OperationType.MINUS,
                    false);
            trade.getChangeAmountParamList().add(marginParam);
        }
    }

    /**
     * 完全成交处理
     *
     * @param bo bo
     * @since 2022/8/20 17:22
     */
    @Override
    public void afterAllDeal(ContractTradeNewBO bo) {
        if (!bo.getIsOpen()) {
            return;
        }

        ContractOrder order = bo.getOrder();

        // 判断是否有未使用的冻结金额
        if (order.getFrozenAmount().compareTo(BigDecimal.ZERO) > 0) {
            WalletContractAccountChangeDTO unfreezeAmount = ContractUtils.getChangeAssetParamByOrder(order,
                    order.getFrozenAmount(),
                    null,
                    null,
                    WalletContractAccountDetailEnum.OperationType.UN_FREEZE,
                    true);
            bo.getChangeAmountParamList().add(unfreezeAmount);
        }
    }
}
